home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacHaskell 2.2 / backend / strictness.scm < prev   
Encoding:
Text File  |  1994-09-27  |  30.1 KB  |  932 lines  |  [TEXT/CCL2]

  1. ;;; strictness.scm -- strictness analyzer
  2. ;;;
  3. ;;; author :  Sandra Loosemore
  4. ;;; date   :  28 May 1992
  5. ;;;
  6. ;;; The algorithm used here follows Consel, "Fast Strictness Analysis
  7. ;;; Via Symbolic Fixpoint Interation".
  8. ;;;
  9. ;;; The basic idea is to do a traversal of the flic structure, building
  10. ;;; a boolean term that represents the strictness of each subexpression.
  11. ;;; The boolean terms are composed of ands & ors of the argument variables
  12. ;;; to each function.  After traversing the body of the function, we can
  13. ;;; determine which argument variables are strict by examining the 
  14. ;;; corresponding term, and then we can update the strictness attribute
  15. ;;; of the var that names the function.
  16. ;;;
  17. ;;; Another traversal needs to be done to attach strictness properties
  18. ;;; to locally bound variables.  
  19.  
  20.  
  21. ;;; Here's the main entry point.
  22.  
  23. (define (strictness-analysis-top big-let)
  24.   (fun-strictness-walk big-let)
  25.   (var-strictness-walk big-let '() '())
  26.   ;; *** This probably belongs somewhere else?
  27.   (do-box-analysis big-let '() '() '#t)
  28.   big-let)
  29.  
  30.  
  31. ;;;======================================================================
  32. ;;; Function strictness analyzer code walk
  33. ;;;======================================================================
  34.  
  35. ;;; This actually involves two code walkers.  The first merely traverses
  36. ;;; structure and identifies function definitions.  The second traverses
  37. ;;; the definitions of the functions to compute their strictness.
  38.  
  39.  
  40. ;;; Fun-strictness-walk is the walker to find function definitions.
  41. ;;; This is trivial for everything other than flic-let.
  42.  
  43. (define-flic-walker fun-strictness-walk (object))
  44.  
  45. (define-fun-strictness-walk flic-lambda (object)
  46.   (fun-strictness-walk (flic-lambda-body object)))
  47.  
  48. (define-fun-strictness-walk flic-let (object)
  49.   (if (flic-let-recursive? object)
  50.       (fun-strictness-walk-letrec object)
  51.       (fun-strictness-walk-let* object))
  52.   (dolist (v (flic-let-bindings object))
  53.     (fun-strictness-walk (var-value v)))
  54.   (fun-strictness-walk (flic-let-body object)))
  55.  
  56. (define-fun-strictness-walk flic-app (object)
  57.   (fun-strictness-walk (flic-app-fn object))
  58.   (for-each (function fun-strictness-walk) (flic-app-args object)))
  59.  
  60. (define-fun-strictness-walk flic-ref (object)
  61.   (declare (ignore object))
  62.   '#f)
  63.  
  64. (define-fun-strictness-walk flic-pack (object)
  65.   (declare (ignore object))
  66.   '#f)
  67.  
  68. (define-fun-strictness-walk flic-const (object)
  69.   (declare (ignore object))
  70.   '#f)
  71.  
  72. (define-fun-strictness-walk flic-case-block (object)
  73.   (for-each (function fun-strictness-walk) (flic-case-block-exps object)))
  74.  
  75. (define-fun-strictness-walk flic-return-from (object)
  76.   (fun-strictness-walk (flic-return-from-exp object)))
  77.  
  78. (define-fun-strictness-walk flic-and (object)
  79.   (for-each (function fun-strictness-walk) (flic-and-exps object)))
  80.  
  81. (define-fun-strictness-walk flic-if (object)
  82.   (fun-strictness-walk (flic-if-test-exp object))
  83.   (fun-strictness-walk (flic-if-then-exp object))
  84.   (fun-strictness-walk (flic-if-else-exp object)))
  85.  
  86. (define-fun-strictness-walk flic-sel (object)
  87.   (fun-strictness-walk (flic-sel-exp object)))
  88.  
  89. (define-fun-strictness-walk flic-is-constructor (object)
  90.   (fun-strictness-walk (flic-is-constructor-exp object)))
  91.  
  92. (define-fun-strictness-walk flic-con-number (object)
  93.   (fun-strictness-walk (flic-con-number-exp object)))
  94.  
  95. (define-fun-strictness-walk flic-void (object)
  96.   (declare (ignore object))
  97.   '#f)
  98.  
  99. (define-fun-strictness-walk flic-update (object)
  100.   (dolist (s (flic-update-slots object))
  101.     (fun-strictness-walk (cdr s)))
  102.   (fun-strictness-walk (flic-update-exp object)))
  103.  
  104.  
  105. ;;; Here is the magic for let bindings of function definitions.
  106. ;;; Sequential bindings are easy.  For recursive bindings, we must 
  107. ;;; keep track of mutually recursive functions.
  108. ;;; If a function binding has a strictness annotation attached,
  109. ;;; do not mess with it further.
  110.  
  111. (define (fun-strictness-walk-let* object)
  112.   (dolist (var (flic-let-bindings object))
  113.     (let ((val  (var-value var)))
  114.       (when (is-type? 'flic-lambda val)
  115.     (if (var-strictness var)
  116.         (mark-argument-strictness
  117.           (var-strictness var) (flic-lambda-vars val))
  118.         (compute-function-strictness var val '())))
  119.       )))
  120.  
  121. (define (fun-strictness-walk-letrec object)
  122.   (let ((stack   '()))
  123.     (dolist (var (flic-let-bindings object))
  124.       (let ((val  (var-value var)))
  125.     (if (and (is-type? 'flic-lambda val) (not (var-strictness var)))
  126.         (setf stack (add-recursive-function-1 var (init-var-env) stack)))))
  127.     (dolist (var (flic-let-bindings object))
  128.       (let ((val  (var-value var)))
  129.     (when (is-type? 'flic-lambda val)
  130.       (if (var-strictness var)
  131.           (mark-argument-strictness
  132.             (var-strictness var) (flic-lambda-vars val))
  133.           (compute-function-strictness var val stack)))
  134.     ))))
  135.  
  136. (define (compute-function-strictness var val stack)
  137.   (let* ((vars  (flic-lambda-vars val))
  138.      (env   (add-var-binding-n vars (map (function list) vars)
  139.                    (init-var-env)))
  140.      (term  (compute-strictness-walk (flic-lambda-body val) env stack)))
  141.     (when (eq? term '#t)
  142.       (signal-infinite-loop-function var)
  143.       (setf (flic-lambda-body val)
  144.         (make-infinite-loop-error
  145.           (format '#f "Function ~s has an infinite loop." var))))
  146.     (setf (var-strictness var) (munge-strictness-terms term vars))))
  147.  
  148.  
  149. (define (signal-infinite-loop-function var)
  150.   (recoverable-error 'infinite-loop-function
  151.     "Function ~s has an infinite loop."
  152.     var))
  153.  
  154. (define (make-infinite-loop-error msg)
  155.   (make-flic-app
  156.     (make-flic-ref (core-symbol "error"))
  157.     (list (make-flic-const msg))
  158.     '#t))
  159.  
  160.   
  161. ;;; compute-strictness-walk is the traversal to compute strictness
  162. ;;; terms.
  163. ;;; The purpose of the env is to map locally bound variables onto 
  164. ;;; strictness terms which are expressed as lists of argument variables
  165. ;;; to the function being analyzed.
  166. ;;; The purpose of the stack is to keep track of recursive function
  167. ;;; walks and recognize when we've reached a fixed point.
  168.  
  169. (define-flic-walker compute-strictness-walk (object env stack))
  170.  
  171.  
  172. ;;; Making a function never forces anything.
  173.  
  174. (define-compute-strictness-walk flic-lambda (object env stack)
  175.   (declare (ignore object env stack))
  176.   '#f)
  177.  
  178.  
  179. ;;; For let, add bindings to environment and get strictness of body.
  180.  
  181. (define-compute-strictness-walk flic-let (object env stack)
  182.   (let ((bindings    (flic-let-bindings object))
  183.     (body        (flic-let-body object))
  184.     (recursive?  (flic-let-recursive? object)))
  185.     (if recursive?
  186.     ;; Must add stuff to env and stack before traversing anything.
  187.     (begin
  188.       (dolist (var bindings)
  189.         (setf env (add-var-binding-1 var '#f env)))
  190.       (dolist (var bindings)
  191.         (let ((val  (var-value var)))
  192.           (when (is-type? 'flic-lambda val)
  193.         (setf stack (add-recursive-function-1 var env stack)))))
  194.       (dolist (var bindings)
  195.         (let ((val  (var-value var)))
  196.           (set-var-env var env (compute-strictness-walk val env stack)))))
  197.     ;; Otherwise just do things sequentially.
  198.     ;; Note that even though there is no possibility of recursion
  199.     ;; here, we must add stuff to the stack anyway so that we can
  200.     ;; walk calls in the correct env.
  201.     (dolist (var bindings)
  202.       (let ((val  (var-value var)))
  203.         (when (is-type? 'flic-lambda val)
  204.           (setf stack (add-recursive-function-1 var env stack)))
  205.         (setf env
  206.           (add-var-binding-1
  207.             var (compute-strictness-walk val env stack) env)))))
  208.     (compute-strictness-walk body env stack)))
  209.  
  210.  
  211. ;;; Treat explicit, saturated calls to named functions specially.
  212.  
  213. (define-compute-strictness-walk flic-app (object env stack)
  214.   (let ((fn         (flic-app-fn object))
  215.     (args       (flic-app-args object))
  216.     (saturated? (flic-app-saturated? object)))
  217.     (cond ((and (is-type? 'flic-ref fn) saturated?)
  218.        ;; Special handling for named functions.
  219.        (compute-application-strictness
  220.          (flic-ref-var fn)
  221.          args env stack))
  222.       ((and (is-type? 'flic-pack fn) saturated?)
  223.        ;; Similarly for constructor applications, but we always
  224.        ;; know which arguments are strict in advance.
  225.        (compute-application-strictness-aux
  226.           (con-slot-strict? (flic-pack-con fn))
  227.           args env stack))
  228.       (else
  229.        ;; Otherwise, we know that the function expression is going to
  230.        ;; be forced, but all of its arguments are lazy.  So ignore the
  231.        ;; arguments in computing the strictness of the whole expression.
  232.        (compute-strictness-walk fn env stack)))))
  233.  
  234.  
  235. ;;; The recursive walk to find the fixed-point converges very slowly
  236. ;;; in some pathological cases (e.g., tak).  Set an arbitrary limit on
  237. ;;; the depth of recursion to avoid this.
  238.  
  239. (define *current-strictness-walk-depth* 0)
  240. (define *max-strictness-walk-depth* 8)
  241.  
  242. (define (compute-application-strictness var args env stack)
  243.   (let* ((strictness          (var-strictness var))
  244.      (info                '#f)
  245.      (arg-strictness-list '#f))
  246.     (cond ((eq? var (core-symbol "error"))
  247.        ;; This expression will return bottom no matter what.
  248.        'error)
  249.       (strictness
  250.        ;; We've already completed the walk for this function and
  251.        ;; determined which of its arguments are strict.
  252.        ;; The strictness expression for the application is the
  253.        ;; OR of the strictness of its non-lazy arguments.
  254.        (compute-application-strictness-aux strictness args env stack))
  255.       ((eqv? (dynamic *current-strictness-walk-depth*)
  256.          (dynamic *max-strictness-walk-depth*))
  257.        ;; Give up.
  258.        '#f)
  259.       ((get-recursive-function-trace
  260.          (setf arg-strictness-list
  261.            (map (lambda (a) (compute-strictness-walk a env stack))
  262.             args))
  263.          (setf info (get-recursive-function var stack)))
  264.        ;; We're already tracing this call.  Return true to
  265.        ;; terminate the fixpoint iteration.
  266.        '#t)
  267.       (else
  268.        ;; Otherwise, begin a new trace instance.
  269.        ;; Add stuff to the saved var-env to map references to
  270.        ;; the argument variables to the strictness terms for
  271.        ;; the actual arguments at this call site.
  272.        ;; References to closed-over variables within the function
  273.        ;; use the strictness values that were stored in the env
  274.        ;; at the point of function definition.
  275.        (let* ((env      (get-recursive-function-env info))
  276.           (lambda   (var-value var))
  277.           (body     (flic-lambda-body lambda))
  278.           (vars     (flic-lambda-vars lambda))
  279.           (result   '#f))
  280.          (push-recursive-function-trace arg-strictness-list info)
  281.          (dynamic-let ((*current-strictness-walk-depth*
  282.                  (1+ (dynamic *current-strictness-walk-depth*))))
  283.              (setf result
  284.              (compute-strictness-walk
  285.                body
  286.                (add-var-binding-n vars arg-strictness-list env)
  287.                stack)))
  288.          (pop-recursive-function-trace info)
  289.          result))
  290.       )))
  291.  
  292.  
  293. (define (compute-application-strictness-aux strictness args env stack)
  294.   (make-or-term
  295.     (map (lambda (strict? arg)
  296.        (if strict? (compute-strictness-walk arg env stack) '#f))
  297.      strictness args)))
  298.  
  299.  
  300. ;;; For a reference, look up the term associated with the variable in env.
  301. ;;; If not present in the environment, ignore it; the binding was established
  302. ;;; outside the scope of the function being analyzed.
  303.  
  304. (define-compute-strictness-walk flic-ref (object env stack)
  305.   (declare (ignore stack))
  306.   (get-var-env (flic-ref-var object) env))
  307.     
  308.  
  309. ;;; References to constants or constructors never fail.
  310.  
  311. (define-compute-strictness-walk flic-const (object env stack)
  312.   (declare (ignore object env stack))
  313.   '#f)
  314.  
  315. (define-compute-strictness-walk flic-pack (object env stack)
  316.   (declare (ignore object env stack))
  317.   '#f)
  318.  
  319.  
  320. ;;; The first clause of a case-block is the only one that is always
  321. ;;; executed, so it is the only one that affects the strictness of
  322. ;;; the overall expression.
  323.  
  324. (define-compute-strictness-walk flic-case-block (object env stack)
  325.   (compute-strictness-walk (car (flic-case-block-exps object)) env stack))
  326.  
  327.  
  328. ;;; Return-from fails if its subexpression fails.
  329.  
  330. (define-compute-strictness-walk flic-return-from (object env stack)
  331.   (compute-strictness-walk (flic-return-from-exp object) env stack))
  332.  
  333.  
  334. ;;; For and, the first subexpression is the only one that is always
  335. ;;; executed, so it is the only one that affects the strictness of
  336. ;;; the overall expression.
  337.  
  338. (define-compute-strictness-walk flic-and (object env stack)
  339.   (compute-strictness-walk (car (flic-and-exps object)) env stack))
  340.  
  341.  
  342. ;;; The strictness of an IF is the strictness of the test OR'ed
  343. ;;; with the AND of the strictness of its branches.
  344.  
  345. (define-compute-strictness-walk flic-if (object env stack)
  346.   (make-or-term-2
  347.     (compute-strictness-walk (flic-if-test-exp object) env stack)
  348.     (make-and-term-2
  349.       (compute-strictness-walk (flic-if-then-exp object) env stack)
  350.       (compute-strictness-walk (flic-if-else-exp object) env stack))))
  351.  
  352.  
  353. ;;; Selecting a component of a data structure causes it to be forced,
  354. ;;; so propagate the strictness of the subexpression upwards.
  355.  
  356. (define-compute-strictness-walk flic-sel (object env stack)
  357.   (compute-strictness-walk (flic-sel-exp object) env stack))
  358.  
  359.  
  360. ;;; Is-constructor and con-number force their subexpressions.
  361.  
  362. (define-compute-strictness-walk flic-is-constructor (object env stack)
  363.   (compute-strictness-walk (flic-is-constructor-exp object) env stack))
  364.  
  365. (define-compute-strictness-walk flic-con-number (object env stack)
  366.   (compute-strictness-walk (flic-con-number-exp object) env stack))
  367.  
  368. (define-compute-strictness-walk flic-void (object env stack)
  369.   (declare (ignore object env stack))
  370.   '#f)
  371.  
  372.  
  373. ;;; Update operation forces the object being copied.  Whether slot
  374. ;;; expressions are strict depends on strictness properties of the
  375. ;;; contructor.
  376.  
  377. (define-compute-strictness-walk flic-update (object env stack)
  378.   (let* ((con    (flic-update-con object))
  379.      (strict (con-slot-strict? con))
  380.      (slots  (flic-update-slots object))
  381.      (exp    (flic-update-exp object)))
  382.     (make-or-term-2
  383.       (compute-strictness-walk exp env stack)
  384.       (make-or-term
  385.         (map (lambda (s)
  386.            (let ((i  (car s)))
  387.          (if (list-ref strict i)
  388.              (compute-strictness-walk (cdr s) env stack)
  389.               '#f)))
  390.          slots)))
  391.     ))
  392.  
  393.  
  394.  
  395.  
  396.  
  397. ;;;======================================================================
  398. ;;; Utilities for managing the env
  399. ;;;======================================================================
  400.  
  401. ;;; The env is just an a-list.
  402.  
  403. (define (init-var-env)
  404.   '())
  405.  
  406. (define (add-var-binding-1 var binding env)
  407.   (cons (cons var binding) env))
  408.  
  409. (define (add-var-binding-n vars bindings env)
  410.   (if (null? vars)
  411.       env
  412.       (add-var-binding-n (cdr vars) (cdr bindings)
  413.              (cons (cons (car vars) (car bindings)) env))))
  414.  
  415. (define (get-var-env var env)
  416.   (let ((stuff  (assq var env)))
  417.     (if stuff
  418.     (cdr stuff)
  419.     '#f)))
  420.  
  421. (define (set-var-env var env new-value)
  422.   (let ((stuff  (assq var env)))
  423.     (if stuff
  424.     (setf (cdr stuff) new-value)
  425.     (error "Can't find binding for ~s in environment." var))))
  426.   
  427.  
  428.  
  429. ;;;======================================================================
  430. ;;; Utilities for managing the stack
  431. ;;;======================================================================
  432.  
  433. ;;; For now, the stack is just an a-list too.
  434. ;;; Some sort of hashing scheme could also be used instead of a linear
  435. ;;; search, but if the iteration depth for the fixpoint analysis is
  436. ;;; small, it's probably not worth the trouble.
  437.  
  438. (define (add-recursive-function-1 var env stack)
  439.   (cons (list var env '()) stack))
  440.  
  441. (define (get-recursive-function var stack)
  442.   (or (assq var stack)
  443.       (error "Can't find entry for ~s in stack." var)))
  444.  
  445. (define (get-recursive-function-env entry)
  446.   (cadr entry))
  447.  
  448. (define (push-recursive-function-trace new-args entry)
  449.   (push new-args (caddr entry)))
  450.  
  451. (define (pop-recursive-function-trace entry)
  452.   (pop (caddr entry)))
  453.  
  454. (define (get-recursive-function-trace args entry)
  455.   (get-recursive-function-trace-aux args (caddr entry)))
  456.  
  457. (define (get-recursive-function-trace-aux args list)
  458.   (cond ((null? list)
  459.      '#f)
  460.     ((every (function term=) args (car list))
  461.      '#t)
  462.     (else
  463.      (get-recursive-function-trace-aux args (cdr list)))))
  464.  
  465.  
  466. ;;;======================================================================
  467. ;;; Utilities for boolean terms
  468. ;;;======================================================================
  469.  
  470.  
  471. ;;; A term is either #t, #f, the symbol 'error, or a list of variables 
  472. ;;; (which are implicitly or'ed together).
  473. ;;; #t and 'error are treated identically, except that #t indicates
  474. ;;; failure because of infinite recursion and 'error indicates failure
  475. ;;; due to a call to the error function.
  476. ;;; In general, AND terms add nothing to the result, so to reduce
  477. ;;; needless computation we generally reduce (and a b) to #f.
  478.  
  479. ;;; Make an OR term.  First look for some obvious special cases as an
  480. ;;; efficiency hack, otherwise fall through to more general code.
  481.  
  482. (define (make-or-term terms)
  483.   (cond ((null? terms)
  484.      '#f)
  485.     ((null? (cdr terms))
  486.      (car terms))
  487.     ((eq? (car terms) '#t)
  488.      '#t)
  489.     ((eq? (car terms) 'error)
  490.      'error)
  491.     ((eq? (car terms) '#f)
  492.      (make-or-term (cdr terms)))
  493.     (else
  494.      (make-or-term-2 (car terms) (make-or-term (cdr terms))))))
  495.  
  496. (define (make-or-term-2 term1 term2)
  497.   (cond ((eq? term2 '#t)
  498.      '#t)
  499.     ((eq? term2 'error)
  500.      'error)
  501.     ((eq? term2 '#f)
  502.      term1)
  503.     ((eq? term1 '#t)
  504.      '#t)
  505.     ((eq? term1 'error)
  506.      'error)
  507.     ((eq? term1 '#f)
  508.      term2)
  509.     ;; At this point we know both terms are variable lists.
  510.     ((implies? term2 term1)
  511.      term2)
  512.     ((implies? term1 term2)
  513.      term1)
  514.     (else
  515.      (merge-list-terms term1 term2))))
  516.  
  517.  
  518. ;;;  Merge the two lists, throwing out duplicate variables.
  519.  
  520. (define (merge-list-terms list1 list2)
  521.   (cond ((null? list1)
  522.      list2)
  523.     ((null? list2)
  524.      list1)
  525.     ((eq? (car list1) (car list2))
  526.      (cons (car list1) (merge-list-terms (cdr list1) (cdr list2))))
  527.     ((var< (car list1) (car list2))
  528.      (cons (car list1) (merge-list-terms (cdr list1) list2)))
  529.     (else
  530.      (cons (car list2) (merge-list-terms list1 (cdr list2))))))
  531.  
  532.  
  533. ;;; Helper function: does term1 imply term2?
  534. ;;; True if every subterm of term2 is also included in term1.
  535.  
  536. (define (implies? term1 term2)
  537.   (every (lambda (v2) (memq v2 term1)) term2))
  538.  
  539.  
  540. ;;; Make an AND term.  Because we don't want to build up arbitrarily
  541. ;;; complex AND expressions, basically just compute an OR list that 
  542. ;;; represents the intersection of the subterms.
  543.  
  544. (define (make-and-term terms)
  545.   (cond ((null? terms)
  546.      '#f)
  547.     ((null? (cdr terms))
  548.      (car terms))
  549.     ((eq? (car terms) '#t)
  550.      (make-and-term (cdr terms)))
  551.     ((eq? (car terms) 'error)
  552.      (make-and-term (cdr terms)))
  553.     ((eq? (car terms) '#f)
  554.      '#f)
  555.     (else
  556.      (make-and-term-2 (car terms) (make-and-term (cdr terms))))))
  557.  
  558. (define (make-and-term-2 term1 term2)
  559.   (cond ((eq? term2 '#t)
  560.      term1)
  561.     ((eq? term2 'error)
  562.      term1)
  563.     ((eq? term2 '#f)
  564.      '#f)
  565.     ((eq? term1 '#t)
  566.      term2)
  567.     ((eq? term1 'error)
  568.      term2)
  569.     ((eq? term1 '#f)
  570.      '#f)
  571.     ;; At this point we know both terms are variable lists.
  572.     ((implies? term2 term1)
  573.      term1)
  574.     ((implies? term1 term2)
  575.      term2)
  576.     (else
  577.      (let ((result  '()))
  578.        (dolist (v term1)
  579.          (if (memq v term2)
  580.          (push v result)))
  581.        (if (null? result)
  582.            '#f
  583.            (nreverse result))))
  584.     ))
  585.  
  586.  
  587. ;;; Subterms of an and/or term are always sorted, so that to compare
  588. ;;; two terms we can just compare subterms componentwise.
  589.  
  590. (define (term= term1 term2)
  591.   (or (eq? term1 term2)
  592.       (and (pair? term1)
  593.        (pair? term2)
  594.        (eq? (car term1) (car term2))
  595.        (term= (cdr term1) (cdr term2)))))
  596.  
  597.  
  598. ;;; Variables within an OR-list are sorted alphabetically by names.
  599.  
  600. (define (var< var1 var2)
  601.   (string<? (symbol->string (def-name var1))
  602.         (symbol->string (def-name var2))))
  603.  
  604.  
  605. ;;; Determine which of the vars are present in the term.
  606.  
  607. (define (munge-strictness-terms term vars)
  608.   (map (lambda (v)
  609.      (setf (var-strict? v)
  610.            (cond ((var-force-strict? v)
  611.               '#t)
  612.              ((eq? term '#t)
  613.               '#t)
  614.              ((eq? term 'error)
  615.               '#t)
  616.              ((eq? term '#f)
  617.               '#f)
  618.              ((memq v term)
  619.               '#t)
  620.              (else
  621.               '#f))))
  622.        vars))
  623.  
  624. (define (mark-argument-strictness strictness vars)
  625.   (map (lambda (s v) (setf (var-strict? v) s)) strictness vars))
  626.  
  627.  
  628.  
  629. ;;;======================================================================
  630. ;;; Variable strictness propagation code walk
  631. ;;;======================================================================
  632.  
  633. ;;; Walk the code, marking any vars found in strict contexts as strict.
  634. ;;; Locally bound variables are consed onto the varlist.  This is
  635. ;;; used to determine which variables can be marked as strict when they
  636. ;;; appear in strict contexts.
  637. ;;; When walking something that does not appear in a strict context
  638. ;;; or that is not always evaluated, reinitialize varlist to the empty
  639. ;;; list.
  640. ;;; The stack is used to keep track of variables that have not been
  641. ;;; initialized yet, so that we can detect some kinds of infinite loops.
  642. ;;; When walking something that is not always evaluated, reset this to 
  643. ;;; the empty list.
  644.  
  645. ;;; Notes by jcp:
  646. ;;;  This walker basicly tracks demands from one expression to the next.
  647. ;;;  It does NOT always do a very good job handling conditionals: for
  648. ;;;  example, in
  649. ;;;   let x = foo y in 
  650. ;;;      if b then x+1 else x-1
  651. ;;;   it does not pick up that x is evaluated in both arms of the conditional.
  652.  
  653. ;;;  A variable can be marked strict when a demand for the let which defines
  654. ;;;  the variable will always reach the value itself.  Thus a let
  655. ;;;  statement will add all defined variables to the varlist.
  656. ;;;
  657. ;;;  The stack contains variables from whose definition demand is propagating.
  658. ;;;  This if v is in the stack, the current expression will be demanded whenever
  659. ;;;  v is demanded.
  660.  
  661.  
  662. (define-flic-walker var-strictness-walk (object varlist stack))
  663.  
  664.  
  665.  
  666. ;;; Since the body of the lambda might not be evaluated, reset
  667. ;;; both varlist and stack.  Add lambda vars to varlist so we can
  668. ;;; mark them strict.
  669. ;;; Note that this is only used for anonymous lambdas.  Named functions
  670. ;;; already have had argument strictness determined and we don't want
  671. ;;; to mess with it further.  See flic-let walker below.
  672.  
  673. (define-var-strictness-walk flic-lambda (object varlist stack)
  674.   (declare (ignore varlist stack))
  675.   (let ((vars  (flic-lambda-vars object))
  676.     (body  (flic-lambda-body object)))
  677.     (dolist (v vars)
  678.       (when (var-force-strict? v)
  679.     (setf (var-strict? v) '#t)))
  680.     (var-strictness-walk body vars '())))
  681.  
  682.  
  683. ;;; The basic idea for let is to find the variables that are strict in 
  684. ;;; the body first, and propagate that information backwards to the 
  685. ;;; binding initializers.
  686.  
  687. ;;; This propagation is rather innacurate - what is really needed is to
  688. ;;; do a transitive closure of the variable dependencies in the letrec.
  689. ;;; Instead, take the quick way out and get a poor approximation.  On the
  690. ;;; other hand, this is probably rather unimoprtant in the overall scheme
  691. ;;; of things.
  692.  
  693. ;;; The loop detection is very primitive!
  694.  
  695. (define-var-strictness-walk flic-let (object varlist stack)
  696.   (let ((bindings  (flic-let-bindings object)))
  697.     (var-strictness-walk-let-aux
  698.       bindings
  699.       (flic-let-body object)
  700.       (append bindings varlist)  ; add the bound vars
  701.       stack
  702.       (flic-let-recursive? object))))
  703.  
  704. (define (var-strictness-walk-let-aux bindings body varlist stack recursive?)
  705.   (if (null? bindings)
  706.       (var-strictness-walk body varlist stack)
  707.       (begin
  708.     (var-strictness-walk-let-aux
  709.       (cdr bindings) body varlist (cdr stack) recursive?)
  710.     (let* ((var  (car bindings))
  711.            (val  (var-value var)))
  712.       (cond ((is-type? 'flic-lambda val)
  713.          ;; Just walk the lambda body in fresh environment.
  714.          ;; Avoid calling the generic lambda walker above because
  715.          ;; we've already determined the strictness of the lambda
  716.          ;; variables.
  717.          ;; I think it is OK to mark recursive variables bound to
  718.          ;; functions as strict.
  719.          (var-strictness-walk (flic-lambda-body val) '() '()))
  720.         ((var-strict? var)
  721.          ;; Recursive variables have to be set back to unstrict
  722.          ;; because the value form might contain forward references.
  723.          ;; The box analyzer will set them to strict again if the
  724.          ;; value forms are safe.
  725.          (when recursive? (setf (var-strict? var) '#f))
  726.          (var-strictness-walk val varlist (cons var stack)))
  727.         (else
  728.          (var-strictness-walk val '() (list var)))
  729.         )))))
  730.  
  731. (define-var-strictness-walk flic-app (object varlist stack)
  732.   (let ((fn           (flic-app-fn object))
  733.     (args         (flic-app-args object))
  734.     (saturated?   (flic-app-saturated? object)))
  735.     (cond ((and saturated? (is-type? 'flic-ref fn))
  736.        ;; Strictness of function should be stored on var
  737.        (do-var-strictness-flic-app-aux
  738.          (var-strictness (flic-ref-var fn))
  739.          fn args varlist stack))
  740.       ((and saturated? (is-type? 'flic-pack fn))
  741.        ;; Strictness of constructor should be stored on con
  742.        (do-var-strictness-flic-app-aux
  743.          (con-slot-strict? (flic-pack-con fn))
  744.          fn args varlist stack))
  745.       (else
  746.        ;; All arguments are non-strict
  747.        (var-strictness-walk fn varlist stack)
  748.        (dolist (a args)
  749.          (var-strictness-walk a '() '()))))))
  750.  
  751. (define (do-var-strictness-flic-app-aux strictness fn args varlist stack)
  752.   (when (not strictness)
  753.     (error "Can't find strictness for function ~s." fn))
  754.   (dolist (a args)
  755.     (if (pop strictness)
  756.     (var-strictness-walk a varlist stack)
  757.     (var-strictness-walk a '() '()))))
  758.  
  759.  
  760. (define-var-strictness-walk flic-ref (object varlist stack)
  761.   (let ((var  (flic-ref-var object)))
  762.     (cond ((memq var stack)
  763.        ;; Circular variable definition detected.
  764.        (signal-infinite-loop-variable var)
  765.        (setf (var-value var)
  766.          (make-infinite-loop-error
  767.            (format '#f "Variable ~s has an infinite loop." var))))
  768.       ((memq var varlist)
  769.        (setf (var-strict? var) '#t))
  770.       (else
  771.        '#f))))
  772.  
  773. (define (signal-infinite-loop-variable var)
  774.   (recoverable-error 'infinite-loop-variable
  775.     "Variable ~s has an infinite loop."
  776.     var))
  777.  
  778. (define-var-strictness-walk flic-const (object varlist stack)
  779.   (declare (ignore object varlist stack))
  780.   '#f)
  781.  
  782. (define-var-strictness-walk flic-pack (object varlist stack)
  783.   (declare (ignore object varlist stack))
  784.   '#f)
  785.  
  786. (define-var-strictness-walk flic-case-block (object varlist stack)
  787.   (var-strictness-walk (car (flic-case-block-exps object)) varlist stack)
  788.   (dolist (exp (cdr (flic-case-block-exps object)))
  789.     (var-strictness-walk exp '() '())))
  790.  
  791. (define-var-strictness-walk flic-return-from (object varlist stack)
  792.   (var-strictness-walk (flic-return-from-exp object) varlist stack))
  793.  
  794. (define-var-strictness-walk flic-and (object varlist stack)
  795.   (var-strictness-walk (car (flic-and-exps object)) varlist stack)
  796.   (dolist (exp (cdr (flic-and-exps object)))
  797.     (var-strictness-walk exp '() '())))
  798.  
  799. (define-var-strictness-walk flic-if (object varlist stack)
  800.   (var-strictness-walk (flic-if-test-exp object) varlist stack)
  801.   (var-strictness-walk (flic-if-then-exp object) '() '())
  802.   (var-strictness-walk (flic-if-else-exp object) '() '()))
  803.  
  804. (define-var-strictness-walk flic-sel (object varlist stack)
  805.   (var-strictness-walk (flic-sel-exp object) varlist stack))
  806.  
  807. (define-var-strictness-walk flic-is-constructor (object varlist stack)
  808.   (var-strictness-walk (flic-is-constructor-exp object) varlist stack))
  809.  
  810. (define-var-strictness-walk flic-con-number (object varlist stack)
  811.   (var-strictness-walk (flic-con-number-exp object) varlist stack))
  812.  
  813. (define-var-strictness-walk flic-void (object varlist stack)
  814.   (declare (ignore object varlist stack))
  815.   '#f)
  816.  
  817. (define-var-strictness-walk flic-update (object varlist stack)
  818.   (let* ((con    (flic-update-con object))
  819.      (strict (con-slot-strict? con))
  820.      (slots  (flic-update-slots object))
  821.      (exp    (flic-update-exp object)))
  822.     (dolist (s slots)
  823.       (let ((i  (car s)))
  824.     (if (list-ref strict i)
  825.         (var-strictness-walk (cdr s) varlist stack)
  826.         (var-strictness-walk (cdr s) '() '()))))
  827.     (var-strictness-walk exp varlist stack)))
  828.  
  829.  
  830. ;;; This should be moved someday ...
  831.  
  832. (define (flic-exp-strict-result? val)
  833.   (cond ((is-type? 'flic-ref val)
  834.      (var-strict? (flic-ref-var val)))
  835.     ((is-type? 'flic-sel val)
  836.      (list-ref (con-slot-strict? (flic-sel-con val)) (flic-sel-i val)))
  837.     (else
  838.      '#t)))
  839.  
  840.  
  841. ;;;======================================================================
  842. ;;; Printer support
  843. ;;;======================================================================
  844.  
  845. (define (strictness-analysis-printer big-let)
  846.   (print-strictness big-let 0))
  847.  
  848. (define (print-strictness-list list depth)
  849.   (dolist (o list)
  850.     (print-strictness o depth)))
  851.  
  852. (define (print-strictness-indent depth)
  853.   (dotimes (i (* 2 depth))
  854.     (declare (ignorable i))
  855.     (write-char #\space)))
  856.  
  857. (define (strictness-string bool)
  858.   (if bool "#t" "#f"))
  859.  
  860. (define-flic-walker print-strictness (object depth))
  861.  
  862. (define-print-strictness flic-lambda (object depth)
  863.   (print-strictness-indent depth)
  864.   (format '#t "In anonymous function:~%")
  865.   (print-strictness (flic-lambda-body object) (1+ depth)))
  866.  
  867. (define-print-strictness flic-let (object depth)
  868.   (dolist (var (flic-let-bindings object))
  869.     (let ((val  (var-value var)))
  870.       (if (is-type? 'flic-lambda val)
  871.       (begin
  872.         (print-strictness-indent depth)
  873.         (format '#t "Function ~s has argument strictness ~a.~%"
  874.             var
  875.             (map (function strictness-string) (var-strictness var)))
  876.         (print-strictness (flic-lambda-body val) (1+ depth)))
  877.       (begin
  878.         (print-strictness-indent depth)
  879.         (format '#t "Variable ~s has strictness ~a.~%"
  880.             var
  881.             (strictness-string (var-strict? var)))
  882.         (print-strictness val depth)))))
  883.   (print-strictness (flic-let-body object) depth))
  884.  
  885. (define-print-strictness flic-app (object depth)
  886.   (print-strictness (flic-app-fn object) depth)
  887.   (print-strictness-list (flic-app-args object) depth))
  888.  
  889. (define-print-strictness flic-ref (object depth)
  890.   (declare (ignore object depth))
  891.   '#f)
  892.  
  893. (define-print-strictness flic-const (object depth)
  894.   (declare (ignore object depth))
  895.   '#f)
  896.  
  897. (define-print-strictness flic-pack (object depth)
  898.   (declare (ignore object depth))
  899.   '#f)
  900.  
  901. (define-print-strictness flic-case-block (object depth)
  902.   (print-strictness-list (flic-case-block-exps object) depth))
  903.  
  904. (define-print-strictness flic-return-from (object depth)
  905.   (print-strictness (flic-return-from-exp object) depth))
  906.  
  907. (define-print-strictness flic-and (object depth)
  908.   (print-strictness-list (flic-and-exps object) depth))
  909.  
  910. (define-print-strictness flic-if (object depth)
  911.   (print-strictness (flic-if-test-exp object) depth)
  912.   (print-strictness (flic-if-then-exp object) depth)
  913.   (print-strictness (flic-if-else-exp object) depth))
  914.  
  915. (define-print-strictness flic-sel (object depth)
  916.   (print-strictness (flic-sel-exp object) depth))
  917.  
  918. (define-print-strictness flic-is-constructor (object depth)
  919.   (print-strictness (flic-is-constructor-exp object) depth))
  920.  
  921. (define-print-strictness flic-con-number (object depth)
  922.   (print-strictness (flic-con-number-exp object) depth))
  923.  
  924. (define-print-strictness flic-void (object depth)
  925.   (declare (ignore object depth))
  926.   '#f)
  927.  
  928. (define-print-strictness flic-update (object depth)
  929.   (dolist (s (flic-update-slots object))
  930.     (print-strictness (cdr s) depth))
  931.   (print-strictness (flic-update-exp object) depth))
  932.